package org.moonlight.jvm.classfile.constant;

import org.moonlight.jvm.classfile.ClassFile;
import org.moonlight.jvm.classfile.ClassReader;
import org.moonlight.jvm.classfile.constant.info.ConstantClassInfo;
import org.moonlight.jvm.classfile.constant.info.ConstantNameAndTypeInfo;
import org.moonlight.jvm.classfile.constant.info.ConstantStringInfo;
import org.moonlight.jvm.classfile.constant.info.ConstantUtf8Info;
import org.moonlight.jvm.classfile.constant.info.dynamic.ConstantInvokeDynamicInfo;
import org.moonlight.jvm.classfile.constant.info.dynamic.ConstantMethodHandleInfo;
import org.moonlight.jvm.classfile.constant.info.dynamic.ConstantMethodTypeInfo;
import org.moonlight.jvm.classfile.constant.info.member.ConstantFieldRefInfo;
import org.moonlight.jvm.classfile.constant.info.member.ConstantInterfaceMethodRefInfo;
import org.moonlight.jvm.classfile.constant.info.member.ConstantMethodRefInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantDoubleInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantFloatInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantIntegerInfo;
import org.moonlight.jvm.classfile.constant.info.number.ConstantLongInfo;

/**
 * 常量信息接口。因为常量池中存放的信息各不相同，所以每种常量的格式也不同。但是它们通常都会有固定的格式，有一些共性，需要一个接口来定义一些通用的方法。
 *
 * 常量数据的第一字节是tag，用来区分常量类型。下面是JVM规范给出的常量结构定义:
 * cp_info {
 *     u1 tag
 *     u1 info[]
 * }
 *
 *
 * @author Moonlight
 * @createTime 2023/8/15 9:00
 **/
public interface ConstantInfo {
    /** UTF8 编码的字符串 **/
    int CONSTANT_UTF8 = 1;
    /** 整形字面量 - boolean、byte、char、short等类型都用int存放 **/
    int CONSTANT_INTEGER = 3;
    /** 浮点型字面量 **/
    int CONSTANT_FLOAT = 4;
    /** 长整型字面量 **/
    int CONSTANT_LONG = 5;
    /** 双精度浮点型字面量 **/
    int CONSTANT_DOUBLE = 6;
    /** 类或接口的符号引用 **/
    int CONSTANT_CLASS = 7;
    /** 字符串类型字面量 **/
    int CONSTANT_STRING = 8;
    /** 字段的符号引用 **/
    int CONSTANT_FIELDREF = 9;
    /** 类中方法的符号引用 **/
    int CONSTANT_METHODREF = 10;
    /** 接口中方法的符号引用 **/
    int CONSTANT_INTERFACEMETHODREF = 11;
    /** 字段或方法的部分符号引用 **/
    int CONSTANT_NAMEANDTYPE = 12;
    /** 方法句柄 **/
    int CONSTANT_METHODHANDLE = 15;
    /** 方法类型 **/
    int CONSTANT_METHODTYPE = 16;
    /** 动态方法调用点 **/
    int CONSTANT_INVOKEDYNAMIC = 18;

    /**
     * 使用 类文件数据读取解析器{@link ClassReader} 从 类文件数据{@link ClassFile} 读取解析出 常量信息{@link ConstantInfo}
     *
     * @param reader 类文件数据读取解析器
     * @createTime 10:34 2023/8/15
     * @author moonlight
     **/
    void readInfo(ClassReader reader);
    
    /** 
     * 常量数据的第一字节是tag，用来区分常量类型.
     * 这个方法就是返回常量数据的 tag 字段，用以区分常量是何种类型
     * @return int 常量 tag
     * @createTime 10:35 2023/8/15 
     * @author moonlight
     **/
    int tag();

    /**
     * 使用 类文件数据读取解析器{@link ClassReader} 从 类文件数据{@link ClassFile} 读取解析出 常量信息{@link ConstantInfo}数组
     *
     * @param reader 类文件数据读取解析器
     * @param scp 类文件静态常量池 {@link StaticConstantPool}
     * @return ConstantInfo 常量信息
     * @createTime 10:42 2023/8/15
     * @author moonlight
     **/
    static ConstantInfo readConstantInfo(ClassReader reader, StaticConstantPool scp) {
        int tag = reader.readUint8();
        ConstantInfo info = newConstantInfo(tag, scp);
        info.readInfo(reader);
        return info;
    }

    /** 
     * 从 类文件中的静态常量池 {@link StaticConstantPool} 中读取数据 根据 常量数据类型(常量数据的第一字节是tag，用来区分常量类型)
     * 创建 具体的 常量信息对象
     *
     * @param tag 常量数据类型 - 常量数据的第一字节是tag，用来区分常量类型
     * @param scp 类文件中的静态常量池 {@link StaticConstantPool}
     * @return ConstantInfo 常量信息
     * @createTime 10:55 2023/8/15
     * @author moonlight
     **/
    static ConstantInfo newConstantInfo(int tag, StaticConstantPool scp) {
        switch (tag) {
            case CONSTANT_UTF8:
                return new ConstantUtf8Info();
            case CONSTANT_INTEGER:
                return new ConstantIntegerInfo();
            case CONSTANT_FLOAT:
                return new ConstantFloatInfo();
            case CONSTANT_LONG:
                return new ConstantLongInfo();
            case CONSTANT_DOUBLE:
                return new ConstantDoubleInfo();
            case CONSTANT_CLASS:
                return new ConstantClassInfo(scp);
            case CONSTANT_STRING:
                return new ConstantStringInfo(scp);
            case CONSTANT_FIELDREF:
                return new ConstantFieldRefInfo(scp);
            case CONSTANT_METHODREF:
                return new ConstantMethodRefInfo(scp);
            case CONSTANT_INTERFACEMETHODREF:
                return new ConstantInterfaceMethodRefInfo(scp);
            case CONSTANT_NAMEANDTYPE:
                return new ConstantNameAndTypeInfo();
            case CONSTANT_METHODTYPE:
                return new ConstantMethodTypeInfo();
            case CONSTANT_METHODHANDLE:
                return new ConstantMethodHandleInfo();
            case CONSTANT_INVOKEDYNAMIC:
                return new ConstantInvokeDynamicInfo();
            default:
                throw new ClassFormatError("Unrecognized Constant Pool Tag");
        }
    }

}
